home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Testing & Debugging / General tools / Audit app & dcmd / Src / TextEditManager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-17  |  10.1 KB  |  434 lines  |  [TEXT/KAHL]

  1. /*                                TextEditManager.c                                */
  2. /*
  3.  * TextEditManager.c
  4.  * Copyright © 1993 Apple Computer Inc. All rights reserved.
  5.  *
  6.  * These routines do some of the busy-work around a TextEdit handle.
  7.  *
  8.  * Note regarding Undo. Unfortunately, there is no refCon or similar in a TextEdit
  9.  * record. This requires us to define a more complex structure (with a TextEdit
  10.  * handle as one of its components) and the application must call a function
  11.  * -- actually a macro -- to access the actual TextEdit handle.
  12.  *
  13.  */
  14. #include "LogManager.h"
  15. #ifndef THINK_C                /* MPW includes            */
  16. #include <Errors.h>
  17. #include <Script.h>
  18. #include <Types.h>
  19. #include <Resources.h>
  20. #include <QuickDraw.h>
  21. #include <Fonts.h>
  22. #include <Events.h>
  23. #include <Windows.h>
  24. #include <ToolUtils.h>
  25. #include <Memory.h>
  26. #include <Menus.h>
  27. #include <Scrap.h>
  28. #include <TextEdit.h>
  29. #endif
  30. #include "TextEditManager.h"
  31. #pragma segment TextEditManager
  32.  
  33. #define width(r)                ((r).right - (r).left)
  34. #define height(r)                ((r).bottom - (r).top)
  35. #ifndef TRUE
  36. #define TRUE                    1
  37. #define FALSE                    0
  38. #endif
  39. #define kMargin                    4        /* Inset from viewRect            */
  40. #define EDIT                    (**editHandle)
  41. #define TEHANDLE                (EDIT.teHandle)
  42. #define TEXT                    (**teHandle)
  43. #define    EVENT                    (*eventRecord)
  44. /*
  45.  * TextEdit understands these special characters.
  46.  */
  47. enum {
  48.     kDelete            = 0x08,            /* Backpace        */
  49.     kForwardDelete    = 0x7F,            /* Delete        */
  50.     kFirstArrow        = 0x1C,            /* Left arrow    */
  51.     kLastArrow        = 0x1F            /* Down arrow    */
  52. };
  53.  
  54. enum EditMenu {
  55.     kEditUndo            = 1,
  56.     kEditUnused,
  57.     kEditCut,
  58.     kEditCopy,
  59.     kEditPaste,
  60.     kEditClear
  61. };
  62.  
  63. /*
  64.  * Local functions
  65.  */
  66. static void                    SetupNewUndo(
  67.         EditHandle                editHandle,
  68.         Boolean                    forceNewUndo,
  69.         UndoAction                undoAction
  70.     );
  71. static void                    DoUndo(
  72.         EditHandle                editHandle
  73.     );
  74.  
  75. EditHandle
  76. CreateEditHandle(
  77.         const Rect                *viewRect,
  78.         short                    fontNumber,
  79.         short                    fontSize,
  80.         short                    editMenuID,
  81.         short                    undoTextResID,
  82.         void                    *refCon
  83.     )
  84. {
  85.         register EditHandle        editHandle;
  86.         register TEHandle        teHandle;
  87.         Rect                    displayRect;
  88.         Rect                    destRect;
  89.         
  90.         editHandle = (EditHandle) NewHandleClear(sizeof (EditRecord));
  91.         if (editHandle != NULL) {
  92.             EDIT.editMenuID = editMenuID;
  93.             EDIT.undoTextResID = undoTextResID;
  94.             EDIT.refCon = refCon;
  95.             TextFont(fontNumber);
  96.             TextSize(fontSize);
  97.             TextFace(normal);
  98.             displayRect = *viewRect;
  99.             InsetRect(&displayRect, kMargin, kMargin);
  100.             destRect = displayRect;
  101.             destRect.right -= 2;        /* TextEdit bug            */
  102.             destRect.bottom = 30000;
  103.             teHandle = TENew(&destRect, &displayRect);
  104.             if (teHandle == NULL) {
  105.                 DisposeHandle((Handle) editHandle);
  106.                 editHandle = NULL;
  107.             }
  108.             else {
  109.                 EDIT.teHandle = teHandle;
  110.                 TEAutoView(TRUE, teHandle);
  111.             }
  112.         }
  113.         return (editHandle);
  114. }
  115.  
  116. void
  117. DisposeEditHandle(
  118.         EditHandle                editHandle
  119.     )
  120. {
  121.         register TEHandle        teHandle;
  122.         
  123.         if (editHandle != NULL) {
  124.             if (EDIT.undoHandle != NULL)
  125.                 DisposeHandle((Handle) EDIT.undoHandle);
  126.             teHandle = GetTEHandle(editHandle);
  127.             if (teHandle != NULL)
  128.                 TEDispose(teHandle);
  129.             DisposeHandle((Handle) editHandle);
  130.         }
  131. }
  132.     
  133. Boolean
  134. DoTextEditEvent(
  135.         EditHandle                editHandle,
  136.         const EventRecord        *eventRecord
  137.     )
  138. {
  139.         register TEHandle        teHandle;
  140.         Boolean                    result;
  141.         GrafPtr                    savePort;
  142.         unsigned short            theChar;
  143.         Point                    mousePt;
  144.         Boolean                    extendSelection;
  145. #define    EVENT                    (*eventRecord)
  146.  
  147.         teHandle = GetTEHandle(editHandle);
  148.         result = FALSE;
  149.         switch (EVENT.what) {
  150.         case keyDown:
  151.         case autoKey:
  152.             if (TEXT.active != 0) {
  153.                 theChar = EVENT.message & charCodeMask;
  154.                 if (theChar >= kFirstArrow && theChar <= kLastArrow)
  155.                     EDIT.newUndo = TRUE;                /* Mouse movement        */
  156.                 else {
  157.                     SetupNewUndo(
  158.                         editHandle,
  159.                         (EDIT.undoAction != kUndoTyping),
  160.                         kUndoTyping
  161.                     );
  162.                 }
  163.                 TEKey(theChar, teHandle);
  164.                 result = TRUE;
  165.             }
  166.             break;
  167.         case nullEvent:
  168.             TEIdle(teHandle);
  169.             break;
  170.         case mouseDown:
  171.             if (TEXT.active != 0 && (EVENT.modifiers & cmdKey) == 0) {
  172.                 GetPort(&savePort);
  173.                 SetPort(TEXT.inPort);
  174.                 mousePt = EVENT.where;
  175.                 GlobalToLocal(&mousePt);
  176.                 if (PtInRect(mousePt, &TEXT.viewRect)) {
  177.                     extendSelection = (EVENT.modifiers & shiftKey) != 0;
  178.                     TEClick(mousePt, extendSelection, teHandle);
  179.                     EDIT.newUndo = TRUE;
  180.                     result = TRUE;
  181.                 }
  182.                 SetPort(savePort);
  183.             }
  184.             break;
  185.         default:
  186.             break;
  187.         }
  188.         return (result);
  189. }
  190.  
  191.  
  192. Boolean
  193. AdjustEditMenu(
  194.         EditHandle                editHandle
  195.     )
  196. {
  197.         register TEHandle        teHandle;
  198.         MenuHandle                editMenuHandle;
  199.         Boolean                    hasSelection;
  200.         long                    scrapLength;
  201.         long                    scrapOffset;
  202.         Boolean                    undoEnabled;
  203.         Boolean                    result;
  204.         Str255                    undoText;
  205.         
  206.         teHandle = GetTEHandle(editHandle);
  207.         editMenuHandle = GetMenu(EDIT.editMenuID);
  208.         result = FALSE;
  209.         if (editMenuHandle != NULL && TEXT.active != 0) {
  210.             result = TRUE;
  211.             DisableItem(editMenuHandle, kEditUndo);
  212.             DisableItem(editMenuHandle, kEditCut);
  213.             DisableItem(editMenuHandle, kEditCopy);
  214.             DisableItem(editMenuHandle, kEditPaste);
  215.             DisableItem(editMenuHandle, kEditClear);
  216.             /*
  217.              * Cut, Copy, and Clear are active if there's a selection.
  218.              */
  219.             hasSelection = (TEXT.selStart != TEXT.selEnd);
  220.             if (hasSelection) {
  221.                 EnableItem(editMenuHandle, kEditCut);
  222.                 EnableItem(editMenuHandle, kEditCopy);
  223.                 EnableItem(editMenuHandle, kEditClear);
  224.             }
  225.             /*
  226.              * Paste is active if there's something in the scrap.
  227.              */
  228.             scrapLength = GetScrap(NULL, 'TEXT', &scrapOffset);
  229.             if (scrapLength > 0)
  230.                 EnableItem(editMenuHandle, kEditPaste);
  231.             /*
  232.              * Undo is active if there is a handle full of old junk.
  233.              */
  234.             undoEnabled = (EDIT.undoHandle != NULL);
  235.             if (undoEnabled == FALSE)
  236.                 EDIT.undoAction = kUndoByItself;
  237.             else {
  238.                 EnableItem(editMenuHandle, kEditUndo);
  239.             }
  240.             /*
  241.              * Set the correct text in the Edit/Undo menu item.
  242.              */
  243.             GetIndString(undoText, EDIT.undoTextResID, EDIT.undoAction);
  244.             SetItem(editMenuHandle, kEditUndo, undoText);
  245.         }
  246.         return (result);
  247. }
  248.  
  249. /*
  250.  * Note: this ignores a lot of error handling.
  251.  */
  252. Boolean
  253. ManageEditMenu(
  254.         EditHandle                editHandle,
  255.         short                    menuItem        /* 1 = Undo, 3 = Cut, etc.    */
  256.     )
  257. {
  258.         register TEHandle        teHandle;
  259.         GrafPtr                    savePort;
  260.         Boolean                    result;
  261.         
  262.         teHandle = GetTEHandle(editHandle);
  263.         result = (TEXT.active != 0);
  264.         if (result) {
  265.             GetPort(&savePort);
  266.             SetPort(TEXT.inPort);
  267.             switch (menuItem) {
  268.             case kEditUndo:
  269.                 DoUndo(editHandle);
  270.                 break;
  271.             case kEditCut:
  272.                 if (ZeroScrap() == noErr) {
  273.                     SetupNewUndo(editHandle, TRUE, kUndoCut);
  274.                     TECut(teHandle);
  275.                     (void) TEToScrap();
  276.                 }
  277.                 break;
  278.             case kEditCopy:
  279.                 if (ZeroScrap() == noErr) {
  280.                     SetupNewUndo(editHandle, TRUE, kUndoCopy);
  281.                     TECopy(teHandle);
  282.                     (void) TEToScrap();
  283.                 }
  284.                 break;
  285.             case kEditPaste:
  286.                 if (TEFromScrap() == noErr) {
  287.                     SetupNewUndo(editHandle, TRUE, kUndoPaste);
  288.                     TEPaste(teHandle);
  289.                 }
  290.                 break;
  291.             case kEditClear:
  292.                 SetupNewUndo(editHandle, TRUE, kUndoClear);
  293.                 TEDelete(teHandle);
  294.                 break;
  295.             default:
  296.                 result = FALSE;
  297.                 break;
  298.             }
  299.             SetPort(savePort);
  300.         }
  301.         return (result);
  302. }
  303.  
  304. void
  305. RepositionEditItem(
  306.         EditHandle                editHandle,
  307.         const Rect                *viewRect
  308.     )
  309. {
  310.         register TEHandle        teHandle;
  311.         Rect                    displayRect;
  312.         short                    newWidth;
  313.         short                    newHeight;
  314.  
  315.         teHandle = GetTEHandle(editHandle);
  316.         displayRect = *viewRect;
  317.         InsetRect(&displayRect, kMargin, kMargin);
  318.         newWidth = width(displayRect);
  319.         newHeight = height(displayRect);
  320.         InvalRect(&TEXT.viewRect);
  321.         TEXT.viewRect.right = TEXT.viewRect.left + newWidth;
  322.         TEXT.viewRect.bottom = TEXT.viewRect.top + newHeight;
  323.         TEXT.destRect.right = TEXT.destRect.left + newWidth;    
  324.         OffsetRect(
  325.             &TEXT.viewRect,
  326.             displayRect.left - TEXT.viewRect.left,
  327.             displayRect.top - TEXT.viewRect.top
  328.         );
  329.         OffsetRect(
  330.             &TEXT.destRect,
  331.             displayRect.left - TEXT.destRect.left,
  332.             displayRect.top - TEXT.destRect.top
  333.         );
  334.         InvalRect(&TEXT.viewRect);
  335. }
  336.  
  337. /*
  338.  * Undo-related functions (private to TextEditManager.c)
  339.  */
  340.  
  341. /*
  342.  * Save the current state of the TextEdit record so the user can Undo it later.
  343.  */
  344.  static void
  345.  SetupNewUndo(
  346.         EditHandle                editHandle,
  347.         Boolean                    forceNewUndo,
  348.         UndoAction                undoAction
  349.     )
  350. {
  351.         register TEHandle        teHandle;
  352.         Handle                    textHandle;
  353.         Handle                    undoHandle;
  354.  
  355.         teHandle = GetTEHandle(editHandle);
  356.         if (forceNewUndo || EDIT.newUndo) {
  357.             textHandle = TEXT.hText;
  358.             undoHandle = EDIT.undoHandle;
  359.             /*
  360.              * Since this is a new sequence, dump the old undo handle if present.
  361.              */
  362.             if (undoHandle != NULL) {
  363.                 DisposeHandle(undoHandle);
  364.                 EDIT.undoHandle = NULL;
  365.             }
  366.             /*
  367.              * Copy the TextEdit handle. If successful, remember the selection.
  368.              */
  369.             undoHandle = textHandle;
  370.             if (HandToHand(&undoHandle) == noErr) {
  371.                 EDIT.undoHandle = undoHandle;
  372.                 EDIT.newUndo = FALSE;
  373.                 EDIT.undoSelStart = TEXT.selStart;
  374.                 EDIT.undoSelEnd = TEXT.selEnd;
  375.                 EDIT.undoAction = undoAction;
  376.             }
  377.         }
  378. }
  379.  
  380. /*
  381.  * We know we're called with an active TextEdit record and the port is set
  382.  * to the TextEdit port.
  383.  */
  384. static void
  385. DoUndo(
  386.         EditHandle                editHandle
  387.     )
  388. {
  389.         register TEHandle        teHandle;
  390.         Handle                    textHandle;
  391.         Handle                    undoHandle;
  392.         short                    oldSelStart;
  393.         short                    oldSelEnd;
  394.         Rect                    viewRect;
  395.         
  396.         teHandle = GetTEHandle(editHandle);
  397.         undoHandle = EDIT.undoHandle;
  398.         if (undoHandle != NULL) {
  399.             textHandle = TEXT.hText;
  400.             oldSelStart = TEXT.selStart;
  401.             oldSelEnd = TEXT.selEnd;
  402.             /*
  403.              * Swap the TEHandle with the UndoHandle. First, duplicate the
  404.              * TextEdit handle. Then store the undo text in the TextEdit record.
  405.              */
  406.             if (HandToHand(&textHandle) == noErr) {
  407.                 HLock(undoHandle);
  408.                 TESetSelect(0, 0, teHandle);
  409.                 TESetText(*undoHandle, GetHandleSize(undoHandle), teHandle);
  410.                 DisposeHandle(undoHandle);
  411.                 TESetSelect(EDIT.undoSelStart, EDIT.undoSelEnd, teHandle);
  412.                 /*
  413.                  * Force an update so the text area looks like something
  414.                  * actually happened.'
  415.                  */
  416.                 viewRect = TEXT.viewRect;
  417.                 EraseRect(&viewRect);
  418.                 TEUpdate(&viewRect, teHandle);
  419.                 /*
  420.                  * Now, save the "undone" text in the undo buffer and flip
  421.                  * between Undo and Redo menu items.
  422.                  */
  423.                 EDIT.undoHandle = textHandle;
  424.                 EDIT.undoSelStart = oldSelStart;
  425.                 EDIT.undoSelEnd = oldSelEnd;
  426.                 EDIT.undoAction =
  427.                     (EDIT.undoAction <= kUndoClear)
  428.                         ? EDIT.undoAction + kUndoOffset
  429.                         : EDIT.undoAction - kUndoOffset;
  430.                 AdjustEditMenu(editHandle);
  431.             }
  432.         }        
  433. }
  434.